package cnki.tpi.kbatis.thread;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Queue;
import java.util.concurrent.*;

/**
 * ClassName: KbaseThreadPoolManager
 * Description:
 *
 * @author 小黄
 * date: 2020/5/27 14:29
 */
@Component
public class KbaseThreadPoolManager implements BeanFactoryAware {

    //用于从IOC里取对象
    private BeanFactory factory;

    // 线程池维护线程的最少数量
    private final static int CORE_POOL_SIZE = 2;
    // 线程池维护线程的最大数量
    private final static int MAX_POOL_SIZE = 10;
    // 线程池维护线程所允许的空闲时间
    private final static int KEEP_ALIVE_TIME = 0;
    // 线程池所使用的缓冲队列大小
    private final static int WORK_QUEUE_SIZE = 50;

    /**
     * kbase长文本插入缓冲队列,当线程池满了，则将sql及长文本map存入到此缓冲队列
     */
    Queue<Object> longTextQueue = new LinkedBlockingQueue<Object>();

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.factory = beanFactory;
    }

    /**
     * 当线程池的容量满了，执行下面代码，将内容存入到缓冲队列
     */
    final RejectedExecutionHandler handler = new RejectedExecutionHandler() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            //加入到缓冲队列
            longTextQueue.offer(((KbaseThread) r).getLongTextDto());
        }
    };

    /**
     * 创建线程池
     */
    final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME,
            TimeUnit.SECONDS, new ArrayBlockingQueue(WORK_QUEUE_SIZE), this.handler);

    /**
     * 将任务加入线程池
     */
    public void addLongTextDto(String sql, Map<String, String> map) {
        LongTextDto longTextDto = new LongTextDto(sql, map);
        KbaseThread kbaseThread = new KbaseThread(longTextDto);
        threadPool.execute(kbaseThread);
    }

    /**
     * 线程池的定时任务----> 称为(调度线程池)。此线程池支持 定时以及周期性执行任务的需求。
     */
    final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);

    /**
     * 检查(调度线程池)，每秒执行一次，查看缓冲队列是否有记录，则重新加入到线程池
     */
    final ScheduledFuture scheduledFuture = scheduler.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            //判断缓冲队列是否存在记录
            if (!longTextQueue.isEmpty()) {
                //当线程池的队列容量少于WORK_QUEUE_SIZE，则开始把缓冲队列的内容 加入到 线程池
                if (threadPool.getQueue().size() < WORK_QUEUE_SIZE) {
                    LongTextDto longTextDto = (LongTextDto) longTextQueue.poll();
                    KbaseThread kbaseThread = new KbaseThread(longTextDto);
                    threadPool.execute(kbaseThread);
                }
            }
        }
    }, 0, 1, TimeUnit.SECONDS);

    /**
     * 获取消息缓冲队列
     */
    public Queue<Object> getLongTextQueue() {
        return longTextQueue;
    }

    /**
     * 终止线程池+调度线程池
     */
    public void shutdown() {
        //true表示如果定时任务在执行，立即中止，false则等待任务结束后再停止
        scheduledFuture.cancel(false);
        scheduler.shutdown();
        threadPool.shutdown();
    }

}
